// 
// OV-chip 2.0 project
// 
// Digital Security (DS) group at Radboud Universiteit Nijmegen
// 
// Copyright (C) 2008, 2009
// 
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License in file COPYING in this or one of the
// parent directories for more details.
// 
// Created 2.9.08 by Hendrik
// 
// APDU_serializable wrapper around BigInteger
// 
// $Id: APDU_BigInteger.java,v 1.16 2009-06-19 20:37:34 tews Exp $

package ds.ov2.bignat;


import java.math.BigInteger;
import ds.ov2.util.APDU_Serializable;


/** 
 * Mutable {@link APDU_Serializable} wrapper around 
 * {@link java.math.BigInteger}. This is a host data type with 
 * {@link Bignat} as corresponding card data type. Its purpose is
 * to wrap BigInteger arguments and results for the OV-chip protocol layer. 
 * Wrapping and unwrapping will be done by the stub code, which is
 * generated by the idl compiler. Therefore, high-level host code should
 * not at all come in contect with APDU_BigInteger's. High-level host
 * code can use BigIntegers for its computations and can also pass
 * BigIntegers into the method stubs of the OV-chip protocol layer. The 
 * OV-chip protocol layer will send the byte array of the BigInteger to the card,
 * where it is copied into a Bignat, silently performing a data 
 * type conversion.
 * <P>
 *
 * The BigIntegers (which are wrapped here) stay immutable. 
 * Mutability of this class is achived by changing a reference to 
 * a BigInteger.
 * <P>
 *
 * For a number of general topics <a
 * href="package-summary.html#package_description">see also the package
 * description.</a>
 *
 * @author Hendrik Tews
 * @version $Revision: 1.16 $
 * @commitdate $Date: 2009-06-19 20:37:34 $ by $Author: tews $
 * @environment host
 * @CPP no cpp preprocessing needed
 */
public class APDU_BigInteger implements APDU_Serializable {

    /**
     * The wrapped BigInteger instance. The value can 
     * be directly changed from the outside, as long as the new 
     * value fits into {@link #max_size} bytes.
     * The size check will be done when {@link #to_byte_array 
     * to_byte_array} is called.
     * Because BigInteger are immutable, this reference changes when
     * deserialization ({@link #from_byte_array from_byte_array}) completes.
     * <P>
     * The reference might be {@code null} before the first deserialization 
     * completes. It must not be {@code null} when serialization starts.
     */
    public BigInteger value;


    /**
     * Internal (de-)serialization buffer. Holds the BigInteger to 
     * serialize or partial data during deserialization.
     */
    private byte[] buf;


    /**
     * Maximal size of the wrapped {@link BigInteger}. This maximal
     * size must equal the size of the {@link Bignat} that this 
     * BigInteger is sent to or received from. That is, (de-)serialization 
     * uses always byte arrays of size {@code max_size}.
     * <P>
     * Note that {@link #value}{@link BigInteger#toByteArray toByteArray()}
     * might be one byte longer, because toByteArray occacionally adds 
     * a leding zero byte.
     */
    public final short max_size;


    /**
     * Sending constructor. Initializes max_size and value.
     *
     * @param max_size
     * @param value
     */
    public APDU_BigInteger(short max_size, BigInteger value) {
        this.max_size = max_size;
        this.value = value;
    }


    /**
     * Receiving constructor. Initializes only max_size. {@link #value}
     * will stay null until set or deserialization completes.
     *
     * @param max_size
     */
    public APDU_BigInteger(short max_size) {
        this(max_size, null);
    }


    //########################################################################
    // APDU_serializable interface
    // 


    /**
     * Size in bytes necessary to send or receive this BigInteger
     * via the OV-chip protocol layer, see 
     * {@link ds.ov2.util.APDU_Serializable#size APDU_Serializable.size()}.
     *
     * @return {@link #max_size}
     */
    public short size() {
        return max_size;
    }


    /**
     * This is a host data type, nothing is compatible with it.
     * See <a href="../util/APDU_Serializable.html#apdu_compatibility">
     * the compatibility check 
     * explanations</a> and also
     * {@link ds.ov2.util.APDU_Serializable#is_compatible_with 
     * APDU_Serializable.is_compatible_with}.
     *
     * @param o actual argument or result
     * @return always false
     */
    public boolean is_compatible_with(Object o) {
        return false;
    }


    /**
     * Serialization of the wrapped BigInteger
     * for the OV-chip protocol layer. See {@link 
     * ds.ov2.util.APDU_Serializable#to_byte_array 
     * APDU_Serializable.to_byte_array}. Prior to serialization
     * {@link #value} must be non-null.
     *
     * @param len available space in {@code byte_array}
     * @param this_index number of bytes that
     * have already been written in preceeding calls
     * @param byte_array data array to serialize the state into
     * @param byte_index index in {@code byte_array} 
     * @return the number of bytes actually written, except for the case 
     *          where serialization finished by writing precisely 
     *          {@code len} bytes, in this case {@code len + 1} is 
     *          returned.
     */
    public short to_byte_array(short len, short this_index, 
                               byte[] byte_array, short byte_index) {
        // System.out.format("BI len %d ti %d bi %d this = %s\n", 
        //                len, this_index, byte_index,
        //                Misc_host.to_byte_hex_string(value));
        // Init buf when we start serialization.
        if(this_index == 0) {
            buf = value.toByteArray();
            assert buf.length <= max_size ||
                buf.length == max_size +1 && buf[0] == 0;
        }
        
        // Copy this_index and len.
        int count = 0;

        // buf might be shorter than size, which means that we have
        // to generate some leading zeros on the fly. Note however, 
        // that zeros_to_prepend could also be -1, if buf contains 
        // a leading zero.
        int zeros_to_prepend = max_size - buf.length;
        while(this_index + count < zeros_to_prepend && count < len) {
            byte_array[byte_index + count] = 0;
            count++;
        }
        // System.out.format("BI zeros %d index %d mylen %d\n", 
        //                zeros_to_prepend, index, mylen);
        if(count == len) {
            // buf contains at least one byte.
            assert this_index + count < max_size;
            return (short)count;
        }
        // Still here? Continue with buf.
        int cplen = 
            this_index + len > max_size ? 
            max_size - this_index - count : len - count;
        System.arraycopy(buf, this_index + count - zeros_to_prepend, 
                         byte_array, byte_index + count, cplen);
        if(this_index + len == max_size)
            return (short)(len + 1);
        else
            return (short)(cplen + count);
    }


    /**
     * Deserialization of the wrapped BigInteger
     * for the OV-chip protocol layer. See {@link 
     * ds.ov2.util.APDU_Serializable#from_byte_array 
     * APDU_Serializable.from_byte_array}. After deserialization is 
     * completed a new BigInteger is constructed from the received data
     * and assigned to {@link #value}.
     *
     * @param len available data in {@code byte_array}
     * @param this_index number of bytes that
     * have already been read in preceeding calls
     * @param byte_array data array to deserialize from
     * @param byte_index index in {@code byte_array} 
     * @return the number of bytes actually read, except for the case 
     * where deserialization finished by reading precisely 
     * {@code len} bytes, in this case {@code len + 1} is 
     * returned.
     */
    public short from_byte_array(short len, short this_index,
                                 byte[] byte_array, short byte_index) {
        // Initialize buf if we start deserialization.
        if(this_index == 0) {
            // make buf one byte longer to always have a zero sign bit.
            buf = new byte[max_size + 1];
            buf[0] = 0;
        }

        int max = 
            len + this_index > max_size ? max_size - this_index : len;
        System.arraycopy(byte_array, byte_index, buf, this_index + 1, max);

        // Construct the BigInteger when we are ready.
        if(this_index + len >= max_size)
            value = new BigInteger(buf);

        if(this_index + len == max_size)
            return (short)(len + 1);
        else
            return (short)max;
    }

}
